package com.strato.orm.mybatis.v1_0_0.factory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.osgi.framework.Bundle;
import org.osgi.framework.wiring.BundleWiring;

import com.strato.base.osgi.v1_0_0.BundleUtil;
import com.strato.classloader.v1_0_0.DefaultCompositeClassLoader;
import com.strato.logging.v1_0_0.api.Log;
import com.strato.logging.v1_0_0.api.LogFactory;
import com.strato.orm.mybatis.v1_0_0.activator.Activator;
import com.strato.orm.mybatis.v1_0_0.annotation.handler.MybatisMapperAnnotationHandler.LocationDef;

public class SqlSessionFactoryProviderImpl implements SqlSessionFactoryProvider {

	private Log log=LogFactory.getLog(getClass());
	
	private SqlSessionFactory factory=null;
	
	@Override
	public synchronized SqlSessionFactory provide() {
		if(factory!=null){
			return factory;
		}
		SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
		String config=createConfig();
		log.info(config);
		DefaultCompositeClassLoader loader=new DefaultCompositeClassLoader();
		loader.addUserDefinedClassLoader(Activator.getContext().getBundle().adapt(BundleWiring.class).getClassLoader());
		for(LocationDef loc:Activator.getHandler().getLocations().values()){
			String bundleName=loc.bundle;
			Bundle bundle = BundleUtil.getBundle(bundleName, Activator.getContext());
			BundleWiring wire=bundle.adapt(BundleWiring.class);
			loader.addUserDefinedClassLoader(wire.getClassLoader());
		}
		Resources.setDefaultClassLoader(loader);
		factory=builder.build(new StringReader(config));
		return factory;
	}

	private String createConfig() {
		VelocityContext context=new VelocityContext();
		Collection<LocationDef> locations=new ArrayList<LocationDef>();
		locations.addAll(Activator.getHandler().getLocations().values());
		context.put("locations", locations);
		StringWriter sw=new StringWriter();
		String resource="/"+getClass().getPackage().getName().replaceAll("\\.", "/")+"/config.vm";
		InputStream is=getClass().getResourceAsStream(resource);
		InputStreamReader r=new InputStreamReader(is, Charset.forName("UTF-8"));
		try{
			Velocity.evaluate(context, sw, getClass().getName(), r);
		}finally{
			try {
				r.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sw.toString();
	}

	@Override
	public void reset() {
		factory=null;
	}

}
